/* Replace "dll.h" with the name of your header */
#include <windows.h>
#include <sqlite3.h>
#include<scheme.h>
#include<scheme-private.h>
#include<dynload.h>

# define DLLIMPORT __declspec (dllexport)

DLLIMPORT void init_sqlite (scheme* sc);

extern void  addFuncsSQLITE(scheme *sc) ;

/********************************/
 static pointer sqlite_open_scheme(scheme *sc, pointer args){
        pointer filename;
        sqlite3* db = 0;
        int rel = 0;
        pointer list;
        if(args!=sc->NIL) {
           filename = pair_car(args);
           if(is_string(filename)) {
              rel = sqlite3_open(string_value(filename),&db);
              if(rel == SQLITE_OK){
                     list = _cons(sc,mk_string(sc,""),sc->NIL,0);
                     list = _cons(sc,mk_integer(sc,(unsigned int) db),list,0);
                     list = _cons(sc,sc->T,list,0);
                     return list;
              }else{
                     list = _cons(sc,mk_string(sc,""),sc->NIL,0);
                     list = _cons(sc,mk_integer(sc,rel),list,0);
                     list = _cons(sc,sc->F,list,0);
                     return list;
              }
           }
        }
        list = _cons(sc,mk_string(sc,"arguments error!"),sc->NIL,0);
        list = _cons(sc,mk_integer(sc,0),list,0);
        list = _cons(sc,sc->F,list,0);
        return list;
 }
 
 static pointer sqlite_close_scheme(scheme *sc, pointer args){
        unsigned int db;
        //sqlite3* db = 0;
        int rel = 0;
        pointer list;
        char* err_msg = NULL;
        if(args!=sc->NIL) {
           if(is_number(pair_car(args))) {
              db = (unsigned int)rvalue(pair_car(args));
              rel = sqlite3_close((sqlite3*)db);
              if(rel == SQLITE_OK){
                     list = _cons(sc,mk_string(sc,""),sc->NIL,0);
                     list = _cons(sc,mk_integer(sc,0),list,0);
                     list = _cons(sc,sc->T,list,0);
                     return list;
              }else{
                    list = _cons(sc,mk_string(sc,""),sc->NIL,0);
                     list = _cons(sc,mk_integer(sc,rel),list,0);
                     list = _cons(sc,sc->F,list,0);
                     return list;
              }
           }
        }
        list = _cons(sc,mk_string(sc,"arguments error!"),sc->NIL,0);
        list = _cons(sc,mk_integer(sc,0),list,0);
        list = _cons(sc,sc->F,list,0);
        return list;
 }
 
 static pointer sqlite_execute_scheme(scheme *sc, pointer args){
        unsigned int db;
        char* sql = NULL;
        //sqlite3* db = 0;
        pointer list;
        char* err_msg = NULL;
        int rel = 0;
        if(args!=sc->NIL) {
           if(is_number(pair_car(pair_cdr(args))) && is_string(pair_car(args))) {
              db = (unsigned int)rvalue(pair_car(pair_cdr(args)));
              sql = string_value(pair_car(args));
              rel = sqlite3_exec((sqlite3*)db,sql,0,0,&err_msg);
              if(rel == SQLITE_OK){
                 err_msg = err_msg == NULL?"":err_msg;
                 list = _cons(sc,mk_string(sc,err_msg),sc->NIL,0);
                 list = _cons(sc,mk_integer(sc,0),list,0);
                 list = _cons(sc,sc->T,list,0);
              }else{
                 list = _cons(sc,mk_string(sc,err_msg),sc->NIL,0);
                 list = _cons(sc,mk_integer(sc,rel),list,0);
                 list = _cons(sc,sc->F,list,0);
              }
              return list;
           }
        }
        list = _cons(sc,mk_string(sc,"arguments error!"),sc->NIL,0);
        list = _cons(sc,mk_integer(sc,0),list,0);
        list = _cons(sc,sc->F,list,0);
        return list;
 }
 
 static pointer sqlite_query_scheme(scheme *sc, pointer args){
        unsigned int db;
        int row_num = 0,column_num = 0,i = 0,j = 0;
        char* sql = NULL;
        char** result = NULL;
        //sqlite3* db = 0;
        pointer list,tmp;
        char* err_msg = NULL;
        int rel = 0;
        if(args!=sc->NIL) {
           if(is_number(pair_car(pair_cdr(args))) && is_string(pair_car(args))) {
              db = (unsigned int)rvalue(pair_car(pair_cdr(args)));
              sql = string_value(pair_car(args));
              //rel = sqlite3_exec((sqlite3*)db,sql,query_callback,&list,&err_msg);
              rel = sqlite3_get_table((sqlite3*)db,sql,&result,&row_num,&column_num,&err_msg);
              if(rel == SQLITE_OK){
                 err_msg = err_msg == NULL?"":err_msg;
                 if(row_num == 0){
                     list = sc->NIL;
                 }else{
                       for(i = row_num - 1 ;i >= 0 ;--i){
                           for(j = column_num - 1;j >= 0 ;--j){
                                 //return mk_string(sc,result[12]);
                                 if(j == column_num - 1){
                                      tmp = _cons(sc,mk_string(sc,result[column_num * i + j]),sc->NIL,0);
                                 }else{
                                       //return mk_string(sc,result[row_num * i + j]);
                                       tmp = _cons(sc,mk_string(sc,result[column_num * i + j]),tmp,0);
                                 }
                           }
                           //return tmp;
                           if(i == row_num - 1){
                                 list = _cons(sc,tmp,sc->NIL,0);
                           }else{
                                 list = _cons(sc,tmp,list,0);
                           }
                     }
                 }
                 list = _cons(sc,list,sc->NIL,0);
                 list = _cons(sc,mk_integer(sc,column_num),list,0);
                 list = _cons(sc,mk_integer(sc,row_num),list,0);
                 list = _cons(sc,mk_string(sc,err_msg),list,0);
                 list = _cons(sc,mk_integer(sc,0),list,0);
                 list = _cons(sc,sc->T,list,0);
              }else{
                 list = _cons(sc,mk_string(sc,err_msg),sc->NIL,0);
                 list = _cons(sc,mk_integer(sc,rel),list,0);
                 list = _cons(sc,sc->F,list,0);
              }
              sqlite3_free_table(result);
              return list;
           }
        }
        list = _cons(sc,mk_string(sc,"arguments error!"),sc->NIL,0);
        list = _cons(sc,mk_integer(sc,0),list,0);
        list = _cons(sc,sc->F,list,0);
        return list;
 }
 
 /*int query_callback(void* list,int col_num,char** col_value,char** col_name){
     int i = 0;
     point tmp;
     for(i = col_num - 1;i >= 0;--i){
           if(i == col_num - 1){
              tmp = _cons(sc,mk_string(sc,col_value[i]),sc->NIL,0);
           }else{
              tmp = _cons(sc,mk_string(sc,col_value[i]),tmp,0);
           }
     }
     return 0;
 }*/
/********************************/


void addFuncsSQLITE(scheme* sc){
   scheme_define (sc, 
                   sc->global_env,
                   mk_symbol (sc, "sqlite-open"),
                   mk_foreign_func (sc, sqlite_open_scheme)) ;
   scheme_define (sc, 
                sc->global_env, 
                mk_symbol(sc,"sqlite-close"), 
                mk_foreign_func(sc, sqlite_close_scheme)); 
   scheme_define (sc, 
                sc->global_env, 
                mk_symbol(sc,"sqlite-execute"), 
                mk_foreign_func(sc, sqlite_execute_scheme));
   scheme_define (sc, 
                sc->global_env, 
                mk_symbol(sc,"sqlite-query"), 
                mk_foreign_func(sc, sqlite_query_scheme));
   return; 
}

void init_sqlite(scheme* sc){
     addFuncsSQLITE(sc); 
}
